package de.szut.gui.components;

import java.awt.Color;
import java.awt.Font;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.border.Border;

import de.szut.gui.listeners.ClickAnimationListener;
import de.szut.gui.listeners.HoverAnimationListener;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
/**
 * A specially designed label that has a hover effect
 * @author Marc Huisinga
 *
 */
public class HoverLabel extends JLabel{

	/**
	 * Default-Border of the HoverLabel
	 */
	public static final Border BORDER = BorderFactory.createLineBorder(Color.CYAN);
	/**
	 * Default-Opaqueness of the HoverLabel
	 */
	public static final boolean OPAQUE = true;
	/**
	 * Default text-alignment of the HoverLabel
	 */
	public static final int ALIGNMENT = SwingConstants.CENTER;
	/**
	 * Default font of the HoverLabel
	 */
	public static final Font FONT = new Font("Tahoma", Font.PLAIN, 18);
	/**
	 * Default background color of the HoverLabel
	 */
	public static final Color BACKGROUNDCOLOR = Color.BLACK;
	/**
	 * Default foreground color of the HoverLabel
	 */
	public static final Color FOREGROUNDCOLOR = Color.CYAN;
	/**
	 * Default amount of ticks of flashing after a click
	 */
	public static final int FLASHTICKS = 12;
	/**
	 * Default time in ms between flashes after a click
	 */
	public static final int FLASHTICKTIME = 50;
	/**
	 * Default amount of ticks of blinking after a hover entered
	 */
	public static final int BLINKTICKS = 10;
	/**
	 * Default time in ms between blinks after a hover entered
	 */
	public static final int BLINKTICKTIME = 40;
	
	private static final long serialVersionUID = -5483762595610221501L;
	
	private int flashTicks;
	private int flashTickTime;
	private int blinkTicks;
	private int blinkTickTime;
	private boolean clickAnimationRunning;
	private boolean hoverAnimationRunning;
	private ArrayList<ClickAnimationListener> clickAnimationListeners;
	private ArrayList<HoverAnimationListener> hoverAnimationListeners;
	private MouseEvent currentClickEvent;
	private MouseEvent currentHoverEvent;
	private Timer currentHoverTimer;
	private Timer currentClickTimer;
	private TimerTask currentHoverTask;
	private TimerTask currentClickTask;
	
	/**
	 * Constructor
	 * @param text - text of the HoverLabel
	 */
	public HoverLabel(String text) {
		super(text);
		styleButton();
	}
	
	/**
	 * Constructor
	 */
	public HoverLabel() {
		styleButton();
	}
	
	private void styleButton() {
		setOpaque(OPAQUE);
		setHorizontalAlignment(ALIGNMENT);
		setFont(FONT);
		setBackgroundInternally(BACKGROUNDCOLOR);
		setBorder(BORDER);
		setForeground(FOREGROUNDCOLOR);
		flashTicks = FLASHTICKS;
		flashTickTime = FLASHTICKTIME;
		blinkTicks = BLINKTICKS;
		blinkTickTime = BLINKTICKTIME;
		clickAnimationListeners = new ArrayList<ClickAnimationListener>();
		hoverAnimationListeners = new ArrayList<HoverAnimationListener>();
		clickAnimationRunning = false;
		hoverAnimationRunning = false;
		
		addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent event) {
				currentClickEvent = event;
				if (!clickAnimationRunning) {
					clickAnimationRunning = true;
					cancelCurrentHoverTask();
					setBackgroundInternally(Color.WHITE);
					shiftColorOnClick(flashTicks, flashTickTime);
				}
			}
			
			@Override
			public void mouseEntered(MouseEvent event) {
				currentHoverEvent = event;
				if (!clickAnimationRunning && !hoverAnimationRunning) {
					hoverAnimationRunning = true;
					shiftColorOnHover(blinkTicks, blinkTickTime);
				}
			}
			
			@Override
			public void mouseExited(MouseEvent event) {
				if (hoverAnimationRunning) {
					cancelCurrentHoverTask();
				}
			}
		});
	}
	
	private void shiftColorOnHover(final int amount, final int timeBetweenTicks) {
		currentHoverTimer = new Timer();
		currentHoverTask = new TimerTask() {
			private int i = amount;
			private Color currentColor = BACKGROUNDCOLOR;
			
			@Override
			public void run() {
				currentColor = currentColor.brighter();
				setBackgroundInternally(currentColor);
				if (i == 0) {
					fireHoverAnimationListeners();
					currentHoverTimer.cancel();
					currentHoverTimer.purge();
				}
				i--;
			}
			
		};
		currentHoverTimer.scheduleAtFixedRate(currentHoverTask, 0, timeBetweenTicks);
	}
	
	private void shiftColorOnClick(final int amount, final int timeBetweenTicks) {
		currentClickTimer = new Timer();
		currentClickTask = new TimerTask() {
			private int i = amount;
			private Color currentColor = Color.WHITE;
			
			@Override
			public void run() {
				currentColor = currentColor.darker();
				setBackgroundInternally(currentColor);
				if (i == 0) {
					fireClickAnimationListeners();
					setBackgroundInternally(BACKGROUNDCOLOR);
					clickAnimationRunning = false;
					currentClickTimer.cancel();
					currentClickTimer.purge();
				}
				i--;
			}
			
		};
		currentClickTimer.scheduleAtFixedRate(currentClickTask, 0, timeBetweenTicks);
	}
	
	private void cancelCurrentHoverTask() {
		if (currentHoverTimer != null) {
			hoverAnimationRunning = false;
			setBackgroundInternally(BACKGROUNDCOLOR);
			currentHoverTimer.cancel();
			currentHoverTimer.purge();
		}
	}
	
	private void setBackgroundInternally(Color newColor) {
		super.setBackground(newColor);
	}
	
	private void fireClickAnimationListeners() {
		for (ClickAnimationListener currentListener : clickAnimationListeners) {
			currentListener.mouseClicked(currentClickEvent);
		}
	}
	
	private void fireHoverAnimationListeners() {
		for (HoverAnimationListener currentListener : hoverAnimationListeners) {
			currentListener.mouseEntered(currentHoverEvent);
		}
	}
	
	/**
	 * Gets the amount of ticks for flashing the animation
	 * @return amount of ticks
	 */
	public int getFlashTicks() {
		return flashTicks;
	}

	/**
	 * Sets the amount of ticks for flashing the animation
	 * @param flashTicks - ticks for the flashing of the animation
	 */
	public void setFlashTicks(int flashTicks) {
		this.flashTicks = flashTicks;
	}

	/**
	 * Gets the time between flashes in ms
	 * @return time between flashes
	 */
	public int getFlashTickTime() {
		return flashTickTime;
	}

	/**
	 * Sets the time between flashes in ms
	 * @param flashTickTime - time between flashes
	 */
	public void setFlashTickTime(int flashTickTime) {
		this.flashTickTime = flashTickTime;
	}

	/**
	 * Gets the amount of ticks for blinking the animation
	 * @return amount of ticks
	 */
	public int getBlinkTicks() {
		return blinkTicks;
	}

	/**
	 * Sets the amount of ticks for blinking the animation
	 * @param blinkTicks - ticks between blinks
	 */
	public void setBlinkTicks(int blinkTicks) {
		this.blinkTicks = blinkTicks;
	}

	/**
	 * Gets the time between blinks in ms
	 * @return time between blinks
	 */
	public int getBlinkTickTime() {
		return blinkTickTime;
	}

	/**
	 * Sets the time between blinks in ms
	 * @param blinkTickTime - time between ticks
	 */
	public void setBlinkTickTime(int blinkTickTime) {
		this.blinkTickTime = blinkTickTime;
	}

	@Override
	/**
	 * Not used anymore since HoverLabels can only be Black
	 */
	@Deprecated
	public void setBackground(Color newColor) {}
	
	/**
	 * Gets all ClickAnimationListeners
	 * @return ClickAnimationListeners
	 */
	public ArrayList<ClickAnimationListener> getClickAnimationListeners() {
		return clickAnimationListeners;
	}

	/**
	 * Sets all ClickAnimationListeners
	 * @param clickAnimationListeners - ClickAnimationListeners to fire
	 */
	public void setClickAnimationListeners(
			ArrayList<ClickAnimationListener> clickAnimationListeners) {
		this.clickAnimationListeners = clickAnimationListeners;
	}
	
	/**
	 * Adds a ClickAnimationListener
	 * @param listener - listener to add
	 */
	public void addClickAnimationListener(ClickAnimationListener listener) {
		clickAnimationListeners.add(listener);
	}

	/**
	 * Gets all HoverAnimationListeners
	 * @return HoverAnimationListeners
	 */
	public ArrayList<HoverAnimationListener> getHoverAnimationListeners() {
		return hoverAnimationListeners;
	}

	/**
	 * Sets all HoverAnimationListeners
	 * @param hoverAnimationListeners - HoverAnimationListeners to fire
	 */
	public void setHoverAnimationListeners(
			ArrayList<HoverAnimationListener> hoverAnimationListeners) {
		this.hoverAnimationListeners = hoverAnimationListeners;
	}
	
	/**
	 * Adds a HoverAnimationListener
	 * @param listener - adds a listener to fire
	 */
	public void addHoverAnimationListener(HoverAnimationListener listener) {
		hoverAnimationListeners.add(listener);
	}
}
